﻿/**
 * @file		dd_pmc_spi.c
 * @brief		SPI(Serial Peripheral Interface) driver
 * @note		None
 * @attention	None
 * 
 * <B><I>Copyright 2016 Socionext Inc.</I></B>
 */

#include <string.h> 
#include "dd_pmc_spi.h"
#include "dd_pmc_hdmac.h"
#include "dd_pmc_crg.h"
#include "dd_arm.h"
#include "pmc.h"

/*----------------------------------------------------------------------*/
/* Definition															*/
/*----------------------------------------------------------------------*/
//---------------------------- driver section ----------------------------
#define D_DD_PMC_SPI_SEND			(0)
#define D_DD_PMC_SPI_RECV			(1)

#define D_DD_PMC_SPI_DMA_TC_MAX		(65536)
#define D_DD_PMC_SPI_WAIT_COUNT		(0x000FFFFF)	// Wait time > 10ms
													//   extSclkM(SPICLK) / (2 * (clk_div + 1) = bps(min)
													//   (100 * 1000000)  / (2 * (255      +1) = 195,313
													//   (1/195,313)*16(bit length(max))*128(word(FIFO max))=10ms

#define D_DD_PMC_SPI_FIFO_SIZE		(16)			// FIFO size:16words

// Bit Location
#define D_DD_PMC_SPI_INT_TXEMPTYPULSE_BIT	(1)
#define D_DD_PMC_SPI_INT_TXWMARKPULSE_BIT	(1 << 1)
#define D_DD_PMC_SPI_INT_RXWMARKPULSE_BIT	(1 << 2)
#define D_DD_PMC_SPI_INT_RXFULLPULSE_BIT	(1 << 3)
#define D_DD_PMC_SPI_INT_XFERDONEPULSE_BIT	(1 << 4)
#define D_DD_PMC_SPI_INT_RXFIFOOVERFLOW_BIT	(1 << 7)
#define D_DD_PMC_SPI_INT_RXTIMEOUT_BIT		(1 << 8)
#define D_DD_PMC_SPI_INT_ALL_BIT_SET		(0x01FF)
#define D_DD_PMC_SPI_INT_TX_CPU_BIT_SET		(D_DD_PMC_SPI_INT_TXEMPTYPULSE_BIT | D_DD_PMC_SPI_INT_TXWMARKPULSE_BIT)
#define D_DD_PMC_SPI_INT_TX_DMA_BIT_SET		(D_DD_PMC_SPI_INT_TX_CPU_BIT_SET)
#define D_DD_PMC_SPI_INT_RX_CPU_BIT_SET		(D_DD_PMC_SPI_INT_RXWMARKPULSE_BIT | D_DD_PMC_SPI_INT_RXFULLPULSE_BIT | D_DD_PMC_SPI_INT_RXFIFOOVERFLOW_BIT)
#define D_DD_PMC_SPI_INT_RX_DMA_BIT_SET		(D_DD_PMC_SPI_INT_RX_CPU_BIT_SET | D_DD_PMC_SPI_INT_RXTIMEOUT_BIT)
#define D_DD_PMC_SPI_INT_FULL_CPU_BIT_SET	(D_DD_PMC_SPI_INT_TX_CPU_BIT_SET | D_DD_PMC_SPI_INT_RX_CPU_BIT_SET)
#define D_DD_PMC_SPI_INT_FULL_DMA_BIT_SET	(D_DD_PMC_SPI_INT_FULL_CPU_BIT_SET | D_DD_PMC_SPI_INT_RXTIMEOUT_BIT)

#define D_DD_PMC_SPI_EN_ENABLEREQ			(1)

#define dd_pmc_spi_dsb() Dd_ARM_Dsb_Pou()

#ifdef CO_DDIM_UTILITY_USE
//---------------------------- utility section ---------------------------
// Nothing Special
//---------------------------- colabo section ----------------------------
#define D_DD_PMC_SPI_DMACA_IS_BASE		(D_DD_PMC_HDMAC_IS_IDREQ_16)

#endif

/*----------------------------------------------------------------------*/
/* Enumeration															*/
/*----------------------------------------------------------------------*/
//---------------------------- driver section ----------------------------
/* Nothing Special */

#ifdef CO_DDIM_UTILITY_USE
//---------------------------- utility section ---------------------------
// Nothing Special
//---------------------------- colabo section ----------------------------
// Nothing Special
#endif

/*----------------------------------------------------------------------*/
/* Structure															*/
/*----------------------------------------------------------------------*/
//---------------------------- driver section ----------------------------
/* SPI send/receive data information. */
typedef struct{
	UCHAR*					send_addr8;			/* Send data address (8bit length) */
	USHORT*					send_addr16;		/* Send data address (9bit length) */
	UCHAR*					recv_addr8;			/* Receive data address (8bit length) */
	USHORT*					recv_addr16;		/* Receive data address (9bit length) */
	UINT32					num;				/* Number of data */
	UINT32					send_pos;			/* Send data position */
	UINT32					recv_pos;			/* Receive data position */
	UINT32					dummy_count;		/* Number of dummy data */
	VP_PMC_SPI_CALLBACK		psend_callback;		/* Callback function pointer when send complete communication */
	VP_PMC_SPI_CALLBACK		precv_callback;		/* Callback function pointer when receivecomplete communication */
	VP_PMC_SPI_SS_CALLBACK	pcallback_ss;		/* Callback function pointer when ssOut/ssIn is active */
	UCHAR					full_duplex;		/* full duplex enable bit */
	UCHAR					dma_wait;			/* DMA Waiting Flag */
	UCHAR					force_stop_flg;		/* Force stop Flag */
} T_DD_PMC_SPI_INFO;

#ifdef CO_DDIM_UTILITY_USE
//---------------------------- utility section ---------------------------
// Nothing Special
//---------------------------- colabo section ----------------------------
/* SPI DMA transfer count information */
typedef struct{ 
	ULONG	trans_count;
	ULONG	dma_count;
} T_DD_PMC_SPI_DMA_COUNT_INFO;

/* SPI DMA transfer information */
typedef struct{ 
	T_DD_PMC_SPI_DMA_COUNT_INFO		count_info[3];
	T_DD_PMC_HDMAC_CTRL				hdmac_ctrl;
	UINT32							count;
	UINT32							index;
	UINT32							index2;
	INT32							result;
	UCHAR							dma_ch;
} T_DD_PMC_SPI_DMA_INFO;

/* Spin Lock */
typedef struct {
	ULONG	lock;
	ULONG	dummy[(64 / 4) - 1];
} T_DD_PMC_SPI_SPIN_LOCK;

#endif

/*----------------------------------------------------------------------*/
/* Global Data															*/
/*----------------------------------------------------------------------*/
//---------------------------- driver section ----------------------------
/* SPI send/receive data information. */
static volatile T_DD_PMC_SPI_INFO gDD_PMC_SPI_Info;


#ifdef CO_DDIM_UTILITY_USE
//---------------------------- utility section ---------------------------
// Nothing Special
//---------------------------- colabo section ----------------------------
/* SPI DMA transfer information */
static volatile T_DD_PMC_SPI_DMA_INFO gDD_PMC_SPI_DMA_Info[2];

/* SPI HDMAC channel information. */
static volatile UCHAR gDD_PMC_SPI_DMA_CH_Info;

#endif

/*----------------------------------------------------------------------*/
/* Macro																*/
/*----------------------------------------------------------------------*/
//---------------------------- driver section ----------------------------
/* Nothing Special */

#ifdef CO_DDIM_UTILITY_USE
//---------------------------- utility section ---------------------------
// Nothing Special
//---------------------------- colabo section ----------------------------
// Nothing Special
#endif

/*----------------------------------------------------------------------*/
/* Local Function														*/
/*----------------------------------------------------------------------*/
//---------------------------- driver section ----------------------------
/**
 * End Communication Process
 */
static VOID dd_pmc_spi_end_process( VOID )
{
	// Disable interrupt
	IO_PMC.SPI.INT_EN.word = 0;
	
	// Disable SPI enable
	IO_PMC.SPI.EN.word &= ~(D_DD_PMC_SPI_EN_ENABLEREQ);
	
	// Disable DMA wait
	if( gDD_PMC_SPI_Info.dma_wait == 1 ){
		Dd_PMC_HDMAC_Stop( gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_SEND].dma_ch );
		Dd_PMC_HDMAC_Stop( gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_RECV].dma_ch );
	}

	gDD_PMC_SPI_Info.dma_wait = 0;

	gDD_PMC_SPI_Info.full_duplex = 0;

	gDD_PMC_SPI_Info.force_stop_flg = 0;


	dd_pmc_spi_dsb();
}

VOID dd_pmc_spi_send_data( VOID )
{
	if( IO_PMC.SPI.AUX_CTRL.bit.BITSIZE > E_DD_PMC_SPI_BIT_LEN_8 ){
		while( gDD_PMC_SPI_Info.send_pos < gDD_PMC_SPI_Info.num ){
			// Write to TX Serial Data Register
			IO_PMC.SPI.TXDATA = *(gDD_PMC_SPI_Info.send_addr16 + gDD_PMC_SPI_Info.send_pos);
			dd_pmc_spi_dsb();

			gDD_PMC_SPI_Info.send_pos++;

			if( IO_PMC.SPI.ST.bit.TXFULL ){
				//FIFO is full. Wait next interrupt.
				break;
			}
		}
	}
	else {	// IO_PMC.SPI.AUX_CTRL.bit.BITSIZE <= E_DD_PMC_SPI_BIT_LEN_8
		while( gDD_PMC_SPI_Info.send_pos < gDD_PMC_SPI_Info.num ){
			// Write to TX Serial Data Register
			IO_PMC.SPI.TXDATA = *(gDD_PMC_SPI_Info.send_addr8 + gDD_PMC_SPI_Info.send_pos);
			dd_pmc_spi_dsb();

			gDD_PMC_SPI_Info.send_pos++;

			if( IO_PMC.SPI.ST.bit.TXFULL ){
				//FIFO is full. Wait next interrupt.
				break;
			}
		}
	}
}

/**
 * Check Stop condition
 */
static INT32 dd_pmc_spi_check_stopping_conditions(UINT32 wait_cnt)
{
	if(IO_PMC.SPI.CTRL.bit.MASTER == E_DD_PMC_SPI_TYPE_MASTER){
		if(wait_cnt >= D_DD_PMC_SPI_WAIT_COUNT){
			dd_pmc_spi_end_process();
			Ddim_Print(("SPI Time Out err.\n"));
			return D_DD_PMC_SPI_TIMEOUT;
		}
	}
	if(gDD_PMC_SPI_Info.force_stop_flg == 1){
		dd_pmc_spi_end_process();
		return D_DD_PMC_SPI_FORCE_STOP;
	}

	return D_DDIM_OK;
}


#ifdef CO_DDIM_UTILITY_USE
//---------------------------- utility section ---------------------------
// Nothing Special
//---------------------------- colabo section ----------------------------
/**
 * Start DMA
 */
static INT32 dd_pmc_spi_start_dma( UCHAR dma_ch, T_DD_PMC_HDMAC_CTRL* hdmac_ctrl, VP_CALLBACK dma_callback )
{
	INT32	ret;

	ret = Dd_PMC_HDMAC_Ctrl_Common( dma_ch, hdmac_ctrl );
	if( ret == D_DDIM_OK ){
		ret = Dd_PMC_HDMAC_Start_Async( dma_ch, dma_callback );
		if( ret != D_DDIM_OK ){
			Ddim_Print(("Dd_PMC_HDMAC_Start_Async() error. ret=0x%x\n", ret));
			return ret;
		}
	}
	else{
		Ddim_Print(("Dd_PMC_HDMAC_Ctrl_Common() error. ret=0x%x\n", ret));
		return ret;
	}

	return ret;
}


/**
 * Callback function of DMA Send
 */
static VOID dd_pmc_spi_send_dma_callback( UCHAR dma_ch, USHORT* const status )
{
	INT32	ret;
	UINT32	index;

	if( *status == D_DD_PMC_HDMAC_SS_NORMAL_END ){
		index	= gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_SEND].index;
		gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_SEND].index2++;

		if( gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_SEND].index2 < gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_SEND].count_info[index].dma_count ){
			// Resume DMA
			ret = Dd_PMC_HDMAC_Resume(dma_ch);
			return;
		}

		gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_SEND].index++;
		gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_SEND].index2 = 0;

		if( gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_SEND].index < gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_SEND].count ){
			index = gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_SEND].index;

			// Update source address
			gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_SEND].hdmac_ctrl.src_addr = Dd_PMC_HDMAC_Get_Src_Addr( dma_ch );

			// Update TC
			gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_SEND].hdmac_ctrl.config_a.bit.tc	= gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_SEND].count_info[index].trans_count - 1;

			if( gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_SEND].count_info[index].dma_count > 1 ){
				// Enable reload counter
				gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_SEND].hdmac_ctrl.config_b.bit.rc	= D_DD_PMC_HDMAC_RC_ENABLE;
			}
			else {
				// Disable reload counter
				gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_SEND].hdmac_ctrl.config_b.bit.rc = D_DD_PMC_HDMAC_RC_DISABLE;
			}

			// Start DMA
			ret = dd_pmc_spi_start_dma( dma_ch, (T_DD_PMC_HDMAC_CTRL*)&gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_SEND].hdmac_ctrl, (VP_CALLBACK)dd_pmc_spi_send_dma_callback );
			if( ret == D_DDIM_OK ){
				return;
			}
			else {
				// Set result
				gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_SEND].result = ret;
			}
		}
		else {	// DMA normal complete
			// Set result
			gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_SEND].result = D_DD_PMC_SPI_NORMAL_COMPLETE;
		}
	}
	else {	// DMA status error
		Ddim_Print(("Dd_PMC_HDMAC_Wait_End() stop status error. status=%d\n", *status));

		// Set result
		gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_SEND].result = D_DD_PMC_SPI_DMA_SS_ERROR;
	}

	if( !gDD_PMC_SPI_Info.full_duplex ){		// fullduplex end processing in the receive DMA callback
		if( IO_PMC.SPI.ST.bit.TXEMPTY == 0 ){
			// Wait interrupt(TXEMPTYPULSE bit)
			gDD_PMC_SPI_Info.dma_wait = 2;
			IO_PMC.SPI.INT_EN.word = D_DD_PMC_SPI_INT_TXEMPTYPULSE_BIT;
			dd_pmc_spi_dsb();
		}
		else {
			// Check force stop
			if( gDD_PMC_SPI_Info.force_stop_flg == 1 ){
				gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_SEND].result = D_DD_PMC_SPI_FORCE_STOP;
			}

			// End SPI communication
			dd_pmc_spi_end_process();

			if( gDD_PMC_SPI_Info.psend_callback != NULL ){
				((void(*)())gDD_PMC_SPI_Info.psend_callback)( gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_SEND].result );
			}
		}
	}
}


/**
 * Callback function of DMA Receive
 */
static VOID dd_pmc_spi_recv_dma_callback( UCHAR dma_ch, USHORT* const status )
{
	INT32	ret;
	UINT32	index;
	UINT32	remain_dummy;
	UINT32	write_dummy;

	if( *status == D_DD_PMC_HDMAC_SS_NORMAL_END ){
		index	= gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_RECV].index;
		gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_RECV].index2++;

		if( gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_RECV].index2 < gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_RECV].count_info[index].dma_count ){
			// Resume DMA
			ret = Dd_PMC_HDMAC_Resume( dma_ch );
			return;
		}

		gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_RECV].index++;
		gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_RECV].index2 = 0;

		if( gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_RECV].index < gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_RECV].count ){
			index = gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_RECV].index;

			// Update destination address
			gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_RECV].hdmac_ctrl.dst_addr = Dd_PMC_HDMAC_Get_Dst_Addr(dma_ch);

			// Update TC
			gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_RECV].hdmac_ctrl.config_a.bit.tc	= gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_RECV].count_info[index].trans_count - 1;

			if( gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_RECV].count_info[index].dma_count > 1 ){
				// Enable reload counter
				gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_RECV].hdmac_ctrl.config_b.bit.rc	= D_DD_PMC_HDMAC_RC_ENABLE;
			}
			else {
				// Disable reload counter
				gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_RECV].hdmac_ctrl.config_b.bit.rc = D_DD_PMC_HDMAC_RC_DISABLE;
			}

			// Start DMA
			ret = dd_pmc_spi_start_dma( dma_ch, (T_DD_PMC_HDMAC_CTRL*)&gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_RECV].hdmac_ctrl, (VP_CALLBACK)dd_pmc_spi_recv_dma_callback );
			if( ret == D_DDIM_OK ){
				return;
			}
			else {
				// Set result
				gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_RECV].result = ret;
			}

			if( ( IO_PMC.SPI.CTRL.bit.MASTER == E_DD_PMC_SPI_TYPE_MASTER ) && ( gDD_PMC_SPI_Info.full_duplex == 0 ) ){	// Master receiving
				// SpinLock.
				Dd_ARM_Critical_Section_Start();

				// Set remain dummy count
				if( gDD_PMC_SPI_Info.dummy_count < gDD_PMC_SPI_Info.num ){
					remain_dummy	= gDD_PMC_SPI_Info.num - gDD_PMC_SPI_Info.dummy_count;

					if( remain_dummy < IO_PMC.SPI.FIFO_DPTH.bit.FIFODEPTH ){
						write_dummy = remain_dummy;
					}
					else {
						write_dummy = IO_PMC.SPI.FIFO_DPTH.bit.FIFODEPTH;
					}

					gDD_PMC_SPI_Info.dummy_count += write_dummy;

					// Set Dummy data for master receiving
					IO_PMC.SPI.TX_DWR.word = write_dummy;
					dd_pmc_spi_dsb();
				}

				// SpinUnLock.
				Dd_ARM_Critical_Section_End();
			}
		}
		else {	// DMA normal complete
			// Set result
			gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_RECV].result = D_DD_PMC_SPI_NORMAL_COMPLETE;
		}
	}
	else {	// DMA status error
		Ddim_Print(("Dd_PMC_HDMAC_Wait_End() stop status error. status=%d\n", *status));

		// Set result
		gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_RECV].result = D_DD_PMC_SPI_DMA_SS_ERROR;
	}

	// End DMA transfer process.
	// Check force stop and buffer overflow error
	if( gDD_PMC_SPI_Info.force_stop_flg == 1 ){
		gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_RECV].result = D_DD_PMC_SPI_FORCE_STOP;
	}
	else if( IO_PMC.SPI.ST.bit.RXOVERFLOW == 1 ){
		gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_RECV].result = D_DD_PMC_SPI_RECV_OVERRUN_ERROR;
	}

	// End SPI communication
	dd_pmc_spi_end_process();

	if( gDD_PMC_SPI_Info.precv_callback != NULL ){
		((void(*)())gDD_PMC_SPI_Info.precv_callback)( gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_RECV].result );
	}
}


/**
 * Set DMA Send
 */
static VOID dd_pmc_spi_set_send_dma( UCHAR dma_ch )
{
	UINT32	remain_num;
	UINT32	count = 0;

	gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_SEND].dma_ch	= dma_ch;

	// Set DMA info. 
	gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_SEND].hdmac_ctrl.config_a.bit.is			= D_DD_PMC_HDMAC_IS_IDREQ_3;
	gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_SEND].hdmac_ctrl.config_b.bit.ms			= D_DD_PMC_HDMAC_MS_DEMAND;
	gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_SEND].hdmac_ctrl.config_b.bit.fd			= D_DD_PMC_HDMAC_FD_FIX;
	// omit BT=D_DD_PMC_HDMAC_BT_NORMAL, BC=0, FS=D_DD_PMC_HDMAC_FS_INCR, RS=D_DD_PMC_HDMAC_RS_DISABLE, RD=D_DD_PMC_HDMAC_RD_DISABLE, DP=0

	if( IO_PMC.SPI.AUX_CTRL.bit.BITSIZE > E_DD_PMC_SPI_BIT_LEN_8 ){
		gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_SEND].hdmac_ctrl.config_b.bit.tw		= D_DD_PMC_HDMAC_TW_HWORD;
		gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_SEND].hdmac_ctrl.src_addr			= (ULONG)gDD_PMC_SPI_Info.send_addr16;
	}
	else{
		gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_SEND].hdmac_ctrl.config_b.bit.tw		= D_DD_PMC_HDMAC_TW_BYTE;
		gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_SEND].hdmac_ctrl.src_addr			= (ULONG)gDD_PMC_SPI_Info.send_addr8;
	}
	gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_SEND].hdmac_ctrl.dst_addr				= (ULONG)(&(IO_PMC.SPI.TXDATA));

	remain_num	= gDD_PMC_SPI_Info.num;

	// Set DMA trans count
	if( remain_num > D_DD_PMC_SPI_DMA_TC_MAX ){
		gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_SEND].count_info[count].trans_count	= D_DD_PMC_SPI_DMA_TC_MAX;
		gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_SEND].count_info[count].dma_count		= remain_num >> 16;	// D_DD_PMC_SPI_DMA_TC_MAX(65536)
		remain_num																= remain_num % D_DD_PMC_SPI_DMA_TC_MAX;
		count++;
	}
	if( remain_num != 0 ){
		gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_SEND].count_info[count].trans_count	= remain_num;
		gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_SEND].count_info[count].dma_count		= 1;
		remain_num																= 0;
		count++;
	}

	if( gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_SEND].count_info[0].dma_count > 1 ){
		// Enable reload counter
		gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_SEND].hdmac_ctrl.config_b.bit.rc		= D_DD_PMC_HDMAC_RC_ENABLE;
	}

	gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_SEND].count = count;
}


/**
 * Set DMA Receive
 */
static VOID dd_pmc_spi_set_recv_dma( UCHAR dma_ch )
{
	UINT32	remain_num;
	UINT32	count = 0;

	gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_RECV].dma_ch	= dma_ch;

	// Set DMA info. 
	gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_RECV].hdmac_ctrl.config_a.bit.is			= D_DD_PMC_HDMAC_IS_IDREQ_4;
	gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_RECV].hdmac_ctrl.config_b.bit.ms			= D_DD_PMC_HDMAC_MS_DEMAND;
	gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_RECV].hdmac_ctrl.config_b.bit.fs			= D_DD_PMC_HDMAC_FS_FIX;
	gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_RECV].hdmac_ctrl.config_b.bit.dp			= 8;	// HPROT of destination protection (8:cacheable)	// ToDo:ユーザ設定させるか
	// omit BT=D_DD_PMC_HDMAC_BT_NORMAL, BC=0, FD=D_DD_PMC_HDMAC_FD_INCR, RS=D_DD_PMC_HDMAC_RS_DISABLE, RD=D_DD_PMC_HDMAC_RD_DISABLE

	if( IO_PMC.SPI.AUX_CTRL.bit.BITSIZE > E_DD_PMC_SPI_BIT_LEN_8 ){
		gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_RECV].hdmac_ctrl.config_b.bit.tw		= D_DD_PMC_HDMAC_TW_HWORD;
		gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_RECV].hdmac_ctrl.dst_addr			= (ULONG)gDD_PMC_SPI_Info.recv_addr16;
	}
	else{
		gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_RECV].hdmac_ctrl.config_b.bit.tw		= D_DD_PMC_HDMAC_TW_BYTE;
		gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_RECV].hdmac_ctrl.dst_addr			= (ULONG)gDD_PMC_SPI_Info.recv_addr8;
	}
	gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_RECV].hdmac_ctrl.src_addr				= (ULONG)(&(IO_PMC.SPI.RXDATA));

	remain_num	= gDD_PMC_SPI_Info.num;

	if( remain_num > D_DD_PMC_SPI_DMA_TC_MAX ){
		gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_RECV].count_info[count].trans_count	= D_DD_PMC_SPI_DMA_TC_MAX;
		gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_RECV].count_info[count].dma_count		= remain_num >> 16;	// D_DD_PMC_SPI_DMA_TC_MAX(65536)
		remain_num																= remain_num % D_DD_PMC_SPI_DMA_TC_MAX;
		count++;
	}

	if( remain_num != 0) {
		gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_RECV].count_info[count].trans_count		= remain_num;
		gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_RECV].count_info[count].dma_count			= 1;
		remain_num																	= 0;
		count++;
	}

	if( gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_RECV].count_info[0].dma_count > 1 ){
		// Enable reload counter
		gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_RECV].hdmac_ctrl.config_b.bit.rc			= D_DD_PMC_HDMAC_RC_ENABLE;
	}

	gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_RECV].count = count;
}
#endif


/*----------------------------------------------------------------------*/
/* Grobal Function														*/
/*----------------------------------------------------------------------*/
/**
 * Set SPI Control
 */
INT32 Dd_PMC_SPI_Ctrl( T_DD_PMC_SPI_CTRL const* const spi_ctrl )
{
	INT32 wait;
	union io_pmc_spi_en	spi_en;
#ifdef CO_PARAM_CHECK
	if( spi_ctrl == NULL ){
		Ddim_Assertion(("SPI input param error. [spi_ctrl] = NULL\n"));
		return D_DD_PMC_SPI_INPUT_PARAM_ERROR;
	}
#endif

	memset((VOID*)&gDD_PMC_SPI_Info, 0, sizeof(T_DD_PMC_SPI_INFO));

	// 1) Write Enable Register to disable the SPI port and TX/RX FIFOs
	IO_PMC.SPI.EN.word &= ~(D_DD_PMC_SPI_EN_ENABLEREQ);
	dd_pmc_spi_dsb();

	// 2) Read the Enable Register - it should be 0 before continuing to the next step.
	for( wait = 0; wait < 1000; wait++ ){
		if( IO_PMC.SPI.EN.bit.__ENABLEREQ == 0 ){
			break;
		}
	}
	if( IO_PMC.SPI.EN.bit.__ENABLEREQ != 0 ){
		return D_DD_PMC_SPI_STATUS_ABNORMAL;
	}

	// 3) Clear any interrupts by writing 'h0000_01FF to Interrupt Clear Register.
	IO_PMC.SPI.INT_CLR.word = D_DD_PMC_SPI_INT_ALL_BIT_SET;

	// 4) If using interrupts, enable interrupts by writing the Interrupt Enable Register.
	gDD_PMC_SPI_Info.pcallback_ss	= spi_ctrl->pcallback_ss;
	if( gDD_PMC_SPI_Info.pcallback_ss ){
		IO_PMC.SPI.INT_CLR.word |= D_DD_PMC_SPI_INT_XFERDONEPULSE_BIT;
		IO_PMC.SPI.INT_EN.word = D_DD_PMC_SPI_INT_XFERDONEPULSE_BIT;
	}

	// 5) Set the polarity of the select lines by writing the Slave Select Polarity Register.
	IO_PMC.SPI.SLV_POL.bit.SSPOL = spi_ctrl->ss_info.sspol;

	// 6) If a master, select which slave is to be selected by writing the Slave Select Register.
	IO_PMC.SPI.SLV_SEL.bit.SSOUT = spi_ctrl->ss_info.ssout;

	// 7) If a master, and if a large inter-transfer delay is desired for use with low-performance slaves,
	//    write the Master Inter-Transfer Delay Register.
	if( spi_ctrl->delay_en ){
		IO_PMC.SPI.MS_DLY.bit.MWAIT = spi_ctrl->delay_val;
	}
	else {
		IO_PMC.SPI.MS_DLY.bit.MWAIT = 0;
	}

	// 8) If a master, and if DMA operation is desired, write to the DMA Timeout Register to set the DMA Timeout Value.
	if( spi_ctrl->dma_en ){
		IO_PMC.SPI.DMA_TO.bit.TIMEOUT = spi_ctrl->dma_recv_to;
	}
	else {
		IO_PMC.SPI.DMA_TO.bit.TIMEOUT = 0x00800000;	// default
	}

	// 9) Write Control Register
	// a. inter-transfer delay enable (master)
	IO_PMC.SPI.CTRL.bit.MWAITEN = spi_ctrl->delay_en;

	// b. dmaEnable
	IO_PMC.SPI.CTRL.bit.DMA = spi_ctrl->dma_en;

	// c. master or slave mode
	IO_PMC.SPI.CTRL.bit.MASTER = spi_ctrl->type;

	// d. cpol
	// e. cpha
	switch( spi_ctrl->mode ){
		case E_DD_PMC_SPI_MODE_0_CPOL0_CPHA0:
			IO_PMC.SPI.CTRL.bit.CPOL = 0;
			IO_PMC.SPI.CTRL.bit.CPHA = 0;
			break;

		case E_DD_PMC_SPI_MODE_1_CPOL0_CPHA1:
			IO_PMC.SPI.CTRL.bit.CPOL = 0;
			IO_PMC.SPI.CTRL.bit.CPHA = 1;
			break;

		case E_DD_PMC_SPI_MODE_2_CPOL1_CPHA0:
			IO_PMC.SPI.CTRL.bit.CPOL = 1;
			IO_PMC.SPI.CTRL.bit.CPHA = 0;
			break;

		case E_DD_PMC_SPI_MODE_3_CPOL1_CPHA1:
			IO_PMC.SPI.CTRL.bit.CPOL = 1;
			IO_PMC.SPI.CTRL.bit.CPHA = 1;
			break;

		default:
			return D_DD_PMC_SPI_INPUT_PARAM_ERROR;
	}

	// f. msb1st
	IO_PMC.SPI.CTRL.bit.MSB1ST = spi_ctrl->bit_direction;

	// g. divisorEnable (master)
	if( spi_ctrl->clk_div ){
		IO_PMC.SPI.CTRL.bit.DIVENABLE = 1;
		IO_PMC.SPI.DIV.bit.DIVISOR = spi_ctrl->clk_div;
	}
	else {
		IO_PMC.SPI.CTRL.bit.DIVENABLE = 0;
		IO_PMC.SPI.DIV.bit.DIVISOR = 0xFF;
	}

	// h. continuous transfer enable (master)

	// 10) Write Auxiliary Control Register
	// a. continuous transfer extend (master)
	if( spi_ctrl->ss_info.cont_trans == E_DD_PMC_SPI_CONT_TRANS_ACT_FIFO_EMPTY ){
		IO_PMC.SPI.CTRL.bit.CONTXFER = 1;
		IO_PMC.SPI.AUX_CTRL.bit.CONTXFEREXTEND = 1;
	}
	else if( spi_ctrl->ss_info.cont_trans == E_DD_PMC_SPI_CONT_TRANS_INACT_FIFO_EMPTY ){
		IO_PMC.SPI.CTRL.bit.CONTXFER = 1;
		IO_PMC.SPI.AUX_CTRL.bit.CONTXFEREXTEND = 0;
	}
	else {
		IO_PMC.SPI.CTRL.bit.CONTXFER = 0;
		IO_PMC.SPI.AUX_CTRL.bit.CONTXFEREXTEND = 0;
	}

	// b. bitsize (from 4 to 16 bits)
	IO_PMC.SPI.AUX_CTRL.bit.BITSIZE = spi_ctrl->bit_length;

	// c. inhibit writes to RX FIFO
	IO_PMC.SPI.AUX_CTRL.bit.INHIBITDIN = spi_ctrl->rx_inhibit_en;

	// d. Spi mode
	IO_PMC.SPI.AUX_CTRL.bit.SPIMODE = 0;	// Normal SPI only

	// SPI Format
	IO_PMC.SPI.AUX_CTRL.bit.XFERFORMAT = 0;	// Motorola SPI only

	// Write the Enable Signal Select
	spi_en.word = IO_PMC.SPI.EN.word;
	spi_en.bit.__EXTENSEL = spi_ctrl->enable_sig;
	IO_PMC.SPI.EN.word = spi_en.word;

	// Write TX/RX FIFO Depth
	IO_PMC.SPI.FIFO_DPTH.bit.FIFODEPTH = spi_ctrl->fifo_depth;

	// Write TXTX/RX FIFO Watermark
	IO_PMC.SPI.FIFO_WMK.bit.TXWMARKSET = spi_ctrl->fifo_wmark_tx;
	IO_PMC.SPI.FIFO_WMK.bit.RXWMARKSET = spi_ctrl->fifo_wmark_rx;

	dd_pmc_spi_dsb();

	return D_DDIM_OK;
}


/**
 * Get SPI Control
 */
INT32 Dd_PMC_SPI_Get_Ctrl( T_DD_PMC_SPI_CTRL* const spi_ctrl )
{
#ifdef CO_PARAM_CHECK
	if( spi_ctrl == NULL ){
		Ddim_Assertion(("SPI input param error. [spi_ctrl] = NULL\n"));
		return D_DD_PMC_SPI_INPUT_PARAM_ERROR;
	}
#endif

	spi_ctrl->pcallback_ss = gDD_PMC_SPI_Info.pcallback_ss;

	spi_ctrl->ss_info.sspol = IO_PMC.SPI.SLV_POL.bit.SSPOL;

	spi_ctrl->ss_info.ssout = IO_PMC.SPI.SLV_SEL.bit.SSOUT;

	spi_ctrl->delay_val		= IO_PMC.SPI.MS_DLY.bit.MWAIT;
	spi_ctrl->dma_recv_to	= IO_PMC.SPI.DMA_TO.bit.TIMEOUT;

	spi_ctrl->delay_en		= IO_PMC.SPI.CTRL.bit.MWAITEN;
	spi_ctrl->dma_en			= IO_PMC.SPI.CTRL.bit.DMA;
	spi_ctrl->type			= IO_PMC.SPI.CTRL.bit.MASTER;

	if( IO_PMC.SPI.CTRL.bit.CPOL == 0 ){
		if( IO_PMC.SPI.CTRL.bit.CPHA == 0 ){
			spi_ctrl->mode	= E_DD_PMC_SPI_MODE_0_CPOL0_CPHA0;
		}
		else {
			spi_ctrl->mode	= E_DD_PMC_SPI_MODE_1_CPOL0_CPHA1;
		}
	}
	else {
		if( IO_PMC.SPI.CTRL.bit.CPHA == 0 ){
			spi_ctrl->mode	= E_DD_PMC_SPI_MODE_2_CPOL1_CPHA0;
		}
		else {
			spi_ctrl->mode	= E_DD_PMC_SPI_MODE_3_CPOL1_CPHA1;
		}
	}

	spi_ctrl->bit_direction	= IO_PMC.SPI.CTRL.bit.MSB1ST;
	spi_ctrl->clk_div		= IO_PMC.SPI.CTRL.bit.DIVENABLE;
	spi_ctrl->clk_div		= IO_PMC.SPI.DIV.bit.DIVISOR;

	if( IO_PMC.SPI.CTRL.bit.CONTXFER == 1 ){
		if( IO_PMC.SPI.AUX_CTRL.bit.CONTXFEREXTEND == 1 ){
			spi_ctrl->ss_info.cont_trans = E_DD_PMC_SPI_CONT_TRANS_ACT_FIFO_EMPTY;
		}
		else {
			spi_ctrl->ss_info.cont_trans = E_DD_PMC_SPI_CONT_TRANS_INACT_FIFO_EMPTY;
		}
	}
	else {
		spi_ctrl->ss_info.cont_trans = E_DD_PMC_SPI_CONT_TRANS_INACT_BETWEEN;
	}

	spi_ctrl->bit_length	= IO_PMC.SPI.AUX_CTRL.bit.BITSIZE;
	spi_ctrl->rx_inhibit_en	= IO_PMC.SPI.AUX_CTRL.bit.INHIBITDIN;

	spi_ctrl->enable_sig	= IO_PMC.SPI.EN.bit.__EXTENSEL;

	spi_ctrl->fifo_depth	= IO_PMC.SPI.FIFO_DPTH.bit.FIFODEPTH;
	spi_ctrl->fifo_wmark_tx	= IO_PMC.SPI.FIFO_WMK.bit.TXWMARKSET;
	spi_ctrl->fifo_wmark_rx	= IO_PMC.SPI.FIFO_WMK.bit.RXWMARKSET;

	return D_DDIM_OK;
}


/**
 * Set sending data information
 */
INT32 Dd_PMC_SPI_Set_Send_Data( VOID const* const send_addr, UINT32 num )
{
#ifdef CO_PARAM_CHECK
	if( send_addr == NULL ){
		Ddim_Assertion(("SPI input param error. [send_addr] = NULL\n"));
		return D_DD_PMC_SPI_INPUT_PARAM_ERROR;
	}
	if( num == 0 ){
		Ddim_Assertion(("SPI input param error. [num] = %d\n", num));
		return D_DD_PMC_SPI_INPUT_PARAM_ERROR;
	}
#endif

	if(IO_PMC.SPI.AUX_CTRL.bit.BITSIZE > E_DD_PMC_SPI_BIT_LEN_8){
		gDD_PMC_SPI_Info.send_addr16 = (USHORT*)send_addr;
	}
	else{
		gDD_PMC_SPI_Info.send_addr8 = (UCHAR*)send_addr;
	}

	gDD_PMC_SPI_Info.num = num;
	gDD_PMC_SPI_Info.send_pos = 0;

	return D_DDIM_OK;
}


/**
 * Start sending data
 */
INT32 Dd_PMC_SPI_Start_Send( VP_PMC_SPI_CALLBACK pcallback )
{
	INT32 ret = D_DDIM_OK;
	UINT32 wait_cnt;

	if( IO_PMC.SPI.CTRL.bit.DMA ){
		// For DMA operation
		Ddim_Print(("Dd_PMC_SPI_Start_Send() error. T_DD_PMC_SPI_CTRL.dma_en=1.\n"));
		return D_DD_PMC_SPI_STATUS_ABNORMAL;
	}

	if( pcallback ){
		gDD_PMC_SPI_Info.psend_callback = pcallback;
		
		// Set interrupt(TXEMPTYPULSE, TXWMARKPULSE bit)
		IO_PMC.SPI.INT_EN.word |= D_DD_PMC_SPI_INT_TX_CPU_BIT_SET;
		
		// For interrupt operation
		// 11) Write the Enable Register to enable the SPI port
		IO_PMC.SPI.EN.word |= D_DD_PMC_SPI_EN_ENABLEREQ;

		// Send data
		dd_pmc_spi_send_data();

		dd_pmc_spi_dsb();

		return ret;
	}

	// For polled operation
	// 11) Write the Enable Register to enable the SPI port
	IO_PMC.SPI.EN.word |= D_DD_PMC_SPI_EN_ENABLEREQ;
	dd_pmc_spi_dsb();

	// Send data
	if( IO_PMC.SPI.AUX_CTRL.bit.BITSIZE > E_DD_PMC_SPI_BIT_LEN_8 ){
		while( gDD_PMC_SPI_Info.send_pos < gDD_PMC_SPI_Info.num ){
			// Wait FIFO not full
			wait_cnt = 0;
			while( IO_PMC.SPI.ST.bit.TXFULL ){
				ret = dd_pmc_spi_check_stopping_conditions( wait_cnt );
				if( ret != D_DDIM_OK ){
					return ret;
				}
				wait_cnt++;
			}

			// Write to TX Serial Data Register
			IO_PMC.SPI.TXDATA = *(gDD_PMC_SPI_Info.send_addr16 + gDD_PMC_SPI_Info.send_pos);
			dd_pmc_spi_dsb();

			gDD_PMC_SPI_Info.send_pos++;
		}
	}
	else {	// IO_PMC.SPI.AUX_CTRL.bit.BITSIZE <= E_DD_PMC_SPI_BIT_LEN_8
		while( gDD_PMC_SPI_Info.send_pos < gDD_PMC_SPI_Info.num ){
			// Wait FIFO not full
			wait_cnt = 0;
			while( IO_PMC.SPI.ST.bit.TXFULL ){
				ret = dd_pmc_spi_check_stopping_conditions( wait_cnt );
				if( ret != D_DDIM_OK ){
					return ret;
				}
				wait_cnt++;
			}

			// Write to TX Serial Data Register
			IO_PMC.SPI.TXDATA = *(gDD_PMC_SPI_Info.send_addr8 + gDD_PMC_SPI_Info.send_pos);
			dd_pmc_spi_dsb();

			gDD_PMC_SPI_Info.send_pos++;
		}
	}

	// All data was written
	wait_cnt = 0;
	while( IO_PMC.SPI.ST.bit.TXEMPTY == 0 ){
		// Wait FIFO Empty
		ret = dd_pmc_spi_check_stopping_conditions( wait_cnt );
		if( ret != D_DDIM_OK ){
			return ret;
		}
		wait_cnt++;
	}

	// Check return code
	if( gDD_PMC_SPI_Info.force_stop_flg == 1 ){
		ret = D_DD_PMC_SPI_FORCE_STOP;
	}

	// End SPI communication
	dd_pmc_spi_end_process();

	return ret;
}


/**
 * Set receiving data information
 */
INT32 Dd_PMC_SPI_Set_Recv_Data( VOID const* const recv_addr, UINT32 num )
{
#ifdef CO_PARAM_CHECK
	if( recv_addr == NULL ){
		Ddim_Assertion(("SPI input param error. [recv_addr] = NULL\n"));
		return D_DD_PMC_SPI_INPUT_PARAM_ERROR;
	}
	if( num == 0 ){
		Ddim_Assertion(("SPI input param error. [num] = %d\n", num));
		return D_DD_PMC_SPI_INPUT_PARAM_ERROR;
	}
#endif

	if( IO_PMC.SPI.AUX_CTRL.bit.BITSIZE > E_DD_PMC_SPI_BIT_LEN_8 ){
		gDD_PMC_SPI_Info.recv_addr16 = (USHORT*)recv_addr;
	}
	else{
		gDD_PMC_SPI_Info.recv_addr8 = (UCHAR*)recv_addr;
	}

	gDD_PMC_SPI_Info.num = num;
	gDD_PMC_SPI_Info.recv_pos = 0;
	gDD_PMC_SPI_Info.dummy_count = 0;

	return D_DDIM_OK;
}


/**
 * Start receiving data
 */
INT32 Dd_PMC_SPI_Start_Recv( VP_PMC_SPI_CALLBACK pcallback )
{
	INT32 ret = D_DDIM_OK;
	UINT32 remain_dummy;
	UINT32 write_dummy;
	UINT32 read_count;
	UINT32 remain_num;
	UINT32 wait_cnt;
	UCHAR rx_wmark_set;

	if( IO_PMC.SPI.CTRL.bit.DMA ){
		// For DMA operation
		Ddim_Print(("Dd_PMC_SPI_Start_Recv() error. T_DD_PMC_SPI_CTRL.dma_en=1.\n"));
		return D_DD_PMC_SPI_STATUS_ABNORMAL;
	}

	if( gDD_PMC_SPI_Info.num < IO_PMC.SPI.FIFO_WMK.bit.RXWMARKSET ){
		// Update Rx Watermark Level
		IO_PMC.SPI.FIFO_WMK.bit.RXWMARKSET = gDD_PMC_SPI_Info.num;
		dd_pmc_spi_dsb();
	}

	if( IO_PMC.SPI.CTRL.bit.MASTER == E_DD_PMC_SPI_TYPE_MASTER ){	// Master receiving
		// Set 1st dummy count
		if( gDD_PMC_SPI_Info.num < IO_PMC.SPI.FIFO_DPTH.bit.FIFODEPTH ){
			write_dummy = gDD_PMC_SPI_Info.num;
		}
		else {
			write_dummy = IO_PMC.SPI.FIFO_DPTH.bit.FIFODEPTH;
		}

		gDD_PMC_SPI_Info.dummy_count = write_dummy;

		// Dummy data for master receiving
		IO_PMC.SPI.TX_DWR.word = gDD_PMC_SPI_Info.dummy_count;
	}

	if( pcallback ){
		gDD_PMC_SPI_Info.precv_callback = pcallback;
		
		// For interrupt operation
		// Set interrupt(RXWMARKPULSE, RXFULLPULSE_BIT, RXFIFOOVERFLOW bit)
		IO_PMC.SPI.INT_EN.word |= D_DD_PMC_SPI_INT_RX_CPU_BIT_SET;

		// 11) Write the Enable Register to enable the SPI port
		IO_PMC.SPI.EN.word |= D_DD_PMC_SPI_EN_ENABLEREQ;
		dd_pmc_spi_dsb();

		return ret;
	}

	// For polled operation
	// 11) Write the Enable Register to enable the SPI port
	IO_PMC.SPI.EN.word |= D_DD_PMC_SPI_EN_ENABLEREQ;
	dd_pmc_spi_dsb();

	rx_wmark_set = IO_PMC.SPI.FIFO_WMK.bit.RXWMARKSET;

	while( gDD_PMC_SPI_Info.recv_pos < gDD_PMC_SPI_Info.num ){
		// Get receive data
		read_count = 0;
		if( IO_PMC.SPI.AUX_CTRL.bit.BITSIZE > E_DD_PMC_SPI_BIT_LEN_8 ){
			while( read_count < rx_wmark_set ){
				// Wait FIFO not Empty
				wait_cnt = 0;
				while( IO_PMC.SPI.ST.bit.RXEMPTY ){
					ret = dd_pmc_spi_check_stopping_conditions( wait_cnt );
					if( ret != D_DDIM_OK ){
						return ret;
					}
					wait_cnt++;
				}

				// Read data word from the RX Serial Data Register.
				*(gDD_PMC_SPI_Info.recv_addr16 + read_count) = IO_PMC.SPI.RXDATA;
				read_count++;
			}
		}
		else {	// IO_PMC.SPI.AUX_CTRL.bit.BITSIZE <= E_DD_PMC_SPI_BIT_LEN_8
			while( read_count < rx_wmark_set ){
				// Wait FIFO not Empty
				wait_cnt = 0;
				while( IO_PMC.SPI.ST.bit.RXEMPTY ){
					ret = dd_pmc_spi_check_stopping_conditions( wait_cnt );
					if( ret != D_DDIM_OK ){
						return ret;
					}
					wait_cnt++;
				}

				// Read data word from the RX Serial Data Register.
				*(gDD_PMC_SPI_Info.recv_addr8 + read_count) = IO_PMC.SPI.RXDATA;
				read_count++;
			}
		}

		gDD_PMC_SPI_Info.recv_pos += read_count;

		// Check for buffer overflow error
		if( IO_PMC.SPI.ST.bit.RXOVERFLOW == 1 ){
			// End SPI communication
			dd_pmc_spi_end_process();
			return D_DD_PMC_SPI_RECV_OVERRUN_ERROR;
		}

		if( gDD_PMC_SPI_Info.dummy_count < gDD_PMC_SPI_Info.num ){
			if( IO_PMC.SPI.CTRL.bit.MASTER == E_DD_PMC_SPI_TYPE_MASTER ){	// Master receiving
				// Set remain dummy count
				remain_dummy	= gDD_PMC_SPI_Info.num - gDD_PMC_SPI_Info.dummy_count;

				if( remain_dummy <= rx_wmark_set ){
					write_dummy = remain_dummy;

					// Update Rx Watermark Level
					IO_PMC.SPI.FIFO_WMK.bit.RXWMARKSET = remain_dummy - 1;
					rx_wmark_set = IO_PMC.SPI.FIFO_WMK.bit.RXWMARKSET;
				}
				else {
					write_dummy = rx_wmark_set;
				}

				gDD_PMC_SPI_Info.dummy_count += write_dummy;

				// Dummy data for master receiving
				IO_PMC.SPI.TX_DWR.word = write_dummy;
				dd_pmc_spi_dsb();
			}
		}
		else {
			remain_num = gDD_PMC_SPI_Info.num - gDD_PMC_SPI_Info.recv_pos;

			// All remain data is already exists in Rx FIFO
			if( remain_num <= IO_PMC.SPI.RXFIFO.bit.RX_FIFO_LEVEL ){
				// Get receive data
				if( IO_PMC.SPI.AUX_CTRL.bit.BITSIZE > E_DD_PMC_SPI_BIT_LEN_8 ){
					while( gDD_PMC_SPI_Info.recv_pos < gDD_PMC_SPI_Info.num ){
						// Read data word from the RX Serial Data Register.
						*(gDD_PMC_SPI_Info.recv_addr16 + gDD_PMC_SPI_Info.recv_pos) = IO_PMC.SPI.RXDATA;
						gDD_PMC_SPI_Info.recv_pos++;
					}
				}
				else {	// IO_PMC.SPI.AUX_CTRL.bit.BITSIZE <= E_DD_SPI_BIT_LEN_8
					while( gDD_PMC_SPI_Info.recv_pos < gDD_PMC_SPI_Info.num ){
						// Read data word from the RX Serial Data Register.
						*(gDD_PMC_SPI_Info.recv_addr8 + gDD_PMC_SPI_Info.recv_pos) = IO_PMC.SPI.RXDATA;
						gDD_PMC_SPI_Info.recv_pos++;
					}
				}
			}
		}
	}

	// Check force stop and buffer overflow error
	if( gDD_PMC_SPI_Info.force_stop_flg == 1 ){
		ret = D_DD_PMC_SPI_FORCE_STOP;
	}
	else if( IO_PMC.SPI.ST.bit.RXOVERFLOW == 1 ){
		ret = D_DD_PMC_SPI_RECV_OVERRUN_ERROR;
	}

	// End SPI communication
	dd_pmc_spi_end_process();

	return ret;
}


/**
 * Start full duplex communication
 */
INT32 Dd_PMC_SPI_Start_Full_Duplex( VP_PMC_SPI_CALLBACK psend_callback, VP_PMC_SPI_CALLBACK precv_callback )
{
	INT32 ret = D_DDIM_OK;
	UINT32 send_num;
	UINT32 remain_num;
	UINT32 read_count;
	UINT32 wait_cnt;

	if( IO_PMC.SPI.CTRL.bit.DMA ){
		// For DMA operation
		Ddim_Print(("Dd_PMC_SPI_Start_Full_Duplex() error. T_DD_PMC_SPI_CTRL.dma_en=1.\n"));
		return D_DD_PMC_SPI_STATUS_ABNORMAL;
	}

	gDD_PMC_SPI_Info.full_duplex = 1;

	if( gDD_PMC_SPI_Info.num <= IO_PMC.SPI.FIFO_WMK.bit.RXWMARKSET ){
		// Update Rx Watermark Level
		IO_PMC.SPI.FIFO_WMK.bit.RXWMARKSET = gDD_PMC_SPI_Info.num;
		dd_pmc_spi_dsb();
	}

	if((psend_callback != NULL) && (precv_callback != NULL)){
		gDD_PMC_SPI_Info.psend_callback = psend_callback;
		gDD_PMC_SPI_Info.precv_callback = precv_callback;
		
		// Set interrupt(TXEMPTYPULSE, TXWMARKPULSE, RXWMARKPULSE, RXFIFOOVERFLOW bit)
		IO_PMC.SPI.INT_EN.word |= D_DD_PMC_SPI_INT_FULL_CPU_BIT_SET;
		
		// For interrupt operation
		// 11) Write the Enable Register to enable the SPI port
		IO_PMC.SPI.EN.word |= D_DD_PMC_SPI_EN_ENABLEREQ;

		// Send data
		dd_pmc_spi_send_data();

		dd_pmc_spi_dsb();

		return ret;
	}

	// For polled operation
	// 11) Write the Enable Register to enable the SPI port
	IO_PMC.SPI.EN.word |= D_DD_PMC_SPI_EN_ENABLEREQ;
	dd_pmc_spi_dsb();

	// Set 1st send data count
	if( gDD_PMC_SPI_Info.num < IO_PMC.SPI.FIFO_DPTH.bit.FIFODEPTH ){
		send_num = gDD_PMC_SPI_Info.num;
	}
	else {
		send_num = IO_PMC.SPI.FIFO_DPTH.bit.FIFODEPTH;
	}

	while( ( gDD_PMC_SPI_Info.recv_pos < gDD_PMC_SPI_Info.num ) ||
		   ( gDD_PMC_SPI_Info.send_pos < gDD_PMC_SPI_Info.num ) ){
		// Send 1st send data
		if( IO_PMC.SPI.AUX_CTRL.bit.BITSIZE > E_DD_PMC_SPI_BIT_LEN_8 ){
			while( gDD_PMC_SPI_Info.send_pos < send_num ){
				// Write to TX Serial Data Register
				IO_PMC.SPI.TXDATA = *(gDD_PMC_SPI_Info.send_addr16 + gDD_PMC_SPI_Info.send_pos);
				dd_pmc_spi_dsb();

				gDD_PMC_SPI_Info.send_pos++;
			}
		}
		else {	// IO_PMC.SPI.AUX_CTRL.bit.BITSIZE <= E_DD_PMC_SPI_BIT_LEN_8
			while( gDD_PMC_SPI_Info.send_pos < send_num ){
				// Write to TX Serial Data Register
				IO_PMC.SPI.TXDATA = *(gDD_PMC_SPI_Info.send_addr8 + gDD_PMC_SPI_Info.send_pos);
				dd_pmc_spi_dsb();

				gDD_PMC_SPI_Info.send_pos++;
			}
		}

		// Get receive data
		read_count = 0;
		if( IO_PMC.SPI.AUX_CTRL.bit.BITSIZE > E_DD_PMC_SPI_BIT_LEN_8 ){
			while( read_count < IO_PMC.SPI.FIFO_WMK.bit.RXWMARKSET ){
				// Wait FIFO not Empty
				wait_cnt = 0;
				while( IO_PMC.SPI.ST.bit.RXEMPTY ){
					ret = dd_pmc_spi_check_stopping_conditions( wait_cnt );
					if( ret != D_DDIM_OK ){
						return ret;
					}
					wait_cnt++;
				}

				// Read data word from the RX Serial Data Register.
				*(gDD_PMC_SPI_Info.recv_addr16 + read_count) = IO_PMC.SPI.RXDATA;
				read_count++;
			}
		}
		else {	// IO_PMC.SPI.AUX_CTRL.bit.BITSIZE <= E_DD_PMC_SPI_BIT_LEN_8
			while( read_count < IO_PMC.SPI.FIFO_WMK.bit.RXWMARKSET ){
				// Wait FIFO not Empty
				wait_cnt = 0;
				while( IO_PMC.SPI.ST.bit.RXEMPTY ){
					ret = dd_pmc_spi_check_stopping_conditions( wait_cnt );
					if( ret != D_DDIM_OK ){
						return ret;
					}
					wait_cnt++;
				}

				// Read data word from the RX Serial Data Register.
				*(gDD_PMC_SPI_Info.recv_addr8 + read_count) = IO_PMC.SPI.RXDATA;
				read_count++;
			}
		}

		gDD_PMC_SPI_Info.recv_pos += read_count;

		// Check for buffer overflow error
		if( IO_PMC.SPI.ST.bit.RXOVERFLOW == 1 ){
			// End SPI communication
			dd_pmc_spi_end_process();
			return D_DD_PMC_SPI_RECV_OVERRUN_ERROR;
		}
		
		if( gDD_PMC_SPI_Info.send_pos >= gDD_PMC_SPI_Info.num ){
			remain_num = gDD_PMC_SPI_Info.num - gDD_PMC_SPI_Info.recv_pos;

			// All remain data is already exists in Rx FIFO
			if( remain_num <= IO_PMC.SPI.RXFIFO.bit.RX_FIFO_LEVEL ){
				// Get receive data
				if( IO_PMC.SPI.AUX_CTRL.bit.BITSIZE > E_DD_PMC_SPI_BIT_LEN_8 ){
					while( gDD_PMC_SPI_Info.recv_pos < gDD_PMC_SPI_Info.num ){
						// Read data word from the RX Serial Data Register.
						*(gDD_PMC_SPI_Info.recv_addr16 + gDD_PMC_SPI_Info.recv_pos) = IO_PMC.SPI.RXDATA;
						gDD_PMC_SPI_Info.recv_pos++;
					}
				}
				else {	// IO_PMC.SPI.AUX_CTRL.bit.BITSIZE <= E_DD_PMC_SPI_BIT_LEN_8
					while( gDD_PMC_SPI_Info.recv_pos < gDD_PMC_SPI_Info.num ){
						// Read data word from the RX Serial Data Register.
						*(gDD_PMC_SPI_Info.recv_addr8 + gDD_PMC_SPI_Info.recv_pos) = IO_PMC.SPI.RXDATA;
						gDD_PMC_SPI_Info.recv_pos++;
					}
				}
			}
		}
		else {
			// Set remain send data count
			remain_num = gDD_PMC_SPI_Info.num - gDD_PMC_SPI_Info.send_pos;
			if( remain_num < IO_PMC.SPI.FIFO_DPTH.bit.FIFODEPTH ){
				send_num = remain_num;
			}
			else {
				send_num = IO_PMC.SPI.FIFO_DPTH.bit.FIFODEPTH;
			}

			remain_num = gDD_PMC_SPI_Info.num - gDD_PMC_SPI_Info.recv_pos;
			if( remain_num <= IO_PMC.SPI.FIFO_WMK.bit.RXWMARKSET ){
				// Update Rx Watermark Level
				IO_PMC.SPI.FIFO_WMK.bit.RXWMARKSET = remain_num - 1;
				dd_pmc_spi_dsb();
			}
		}
	}

	// Check force stop and buffer overflow error
	if( gDD_PMC_SPI_Info.force_stop_flg == 1 ){
		ret = D_DD_PMC_SPI_FORCE_STOP;
	}
	else if( IO_PMC.SPI.ST.bit.RXOVERFLOW == 1 ){
		ret = D_DD_PMC_SPI_RECV_OVERRUN_ERROR;
	}

	// End SPI communication
	dd_pmc_spi_end_process();

	return ret;
}


/**
 * Stop sending/receiving data(force stop)
 */
VOID Dd_PMC_SPI_Stop( VOID )
{
	INT32 wait;
	
	if( gDD_PMC_SPI_Info.dma_wait == 1 ){
		Dd_PMC_HDMAC_Stop( gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_SEND].dma_ch );
		Dd_PMC_HDMAC_Stop( gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_RECV].dma_ch );
		gDD_PMC_SPI_Info.dma_wait = 0;
	}

	IO_PMC.SPI.INT_EN.word = 0;

	IO_PMC.SPI.EN.word &= ~(D_DD_PMC_SPI_EN_ENABLEREQ);

	for( wait = 0; wait < 1000; wait++ ){
		if( IO_PMC.SPI.EN.bit.__ENABLEREQ == 0 ){
			break;
		}
	}

	gDD_PMC_SPI_Info.force_stop_flg = 1;

	dd_pmc_spi_dsb();
}


/**
 * Set SPI Slave Select setting
 */
INT32 Dd_PMC_SPI_Set_Slave_Select( T_DD_PMC_SPI_SS_INFO const* const ss_info )
{
#ifdef CO_PARAM_CHECK
	if( ss_info == NULL ){
		Ddim_Assertion(("SPI input param error. [ss_info] = NULL\n"));
		return D_DD_PMC_SPI_INPUT_PARAM_ERROR;
	}
#endif

	IO_PMC.SPI.SLV_POL.bit.SSPOL = ss_info->sspol;

	IO_PMC.SPI.SLV_SEL.bit.SSOUT = ss_info->ssout;

	if( ss_info->cont_trans == E_DD_PMC_SPI_CONT_TRANS_ACT_FIFO_EMPTY ){
		IO_PMC.SPI.CTRL.bit.CONTXFER = 1;
		IO_PMC.SPI.AUX_CTRL.bit.CONTXFEREXTEND = 1;
	}
	else if( ss_info->cont_trans == E_DD_PMC_SPI_CONT_TRANS_INACT_FIFO_EMPTY ){
		IO_PMC.SPI.CTRL.bit.CONTXFER = 1;
		IO_PMC.SPI.AUX_CTRL.bit.CONTXFEREXTEND = 0;
	}
	else {
		IO_PMC.SPI.CTRL.bit.CONTXFER = 0;
		IO_PMC.SPI.AUX_CTRL.bit.CONTXFEREXTEND = 0;
	}

	return D_DDIM_OK;
}


/**
 * Get SPI Slave Select setting
 */
INT32 Dd_PMC_SPI_Get_Slave_Select( T_DD_PMC_SPI_SS_INFO* const ss_info )
{
#ifdef CO_PARAM_CHECK
	if( ss_info == NULL ){
		Ddim_Assertion(("SPI input param error. [ss_info] = NULL\n"));
		return D_DD_PMC_SPI_INPUT_PARAM_ERROR;
	}
#endif

	ss_info->sspol = IO_PMC.SPI.SLV_POL.bit.SSPOL;

	ss_info->ssout = IO_PMC.SPI.SLV_SEL.bit.SSOUT;

	if( IO_PMC.SPI.CTRL.bit.CONTXFER == 1 ){
		if( IO_PMC.SPI.AUX_CTRL.bit.CONTXFEREXTEND == 1 ){
			ss_info->cont_trans = E_DD_PMC_SPI_CONT_TRANS_ACT_FIFO_EMPTY;
		}
		else {
			ss_info->cont_trans = E_DD_PMC_SPI_CONT_TRANS_INACT_FIFO_EMPTY;
		}
	}
	else {
		ss_info->cont_trans = E_DD_PMC_SPI_CONT_TRANS_INACT_BETWEEN;
	}

	return D_DDIM_OK;
}


/**
 * Set SPI clock divisor
 */
VOID Dd_PMC_SPI_Set_Clock_Divisor( UCHAR clk_div )
{
	IO_PMC.SPI.DIV.bit.DIVISOR = clk_div;
}


/**
 * Get SPI clock divisor
 */
INT32 Dd_PMC_SPI_Get_Clock_Divisor( UCHAR* clk_div )
{
#ifdef CO_PARAM_CHECK
	if( clk_div == NULL ){
		Ddim_Assertion(("SPI input param error. [clk_div] = NULL\n"));
		return D_DD_PMC_SPI_INPUT_PARAM_ERROR;
	}
#endif

	*clk_div = IO_PMC.SPI.DIV.bit.DIVISOR;

	return D_DDIM_OK;
}

// Interrupt Handler of SPI.
VOID Dd_PMC_SPI_Int_Handler( VOID )
{
	UINT32 remain_dummy;
	UINT32 write_dummy;
	UINT32 remain_num;
	UINT32 read_count = 0;
	UCHAR rx_wmark_set;

	// ssOut(Master)/ssIn(Slave) interrupt
	if( ( IO_PMC.SPI.INT_EN.bit.XFERDONEPULSE == 1 ) && ( IO_PMC.SPI.INT_ST.bit.XFERDONEPULSE == 1 ) ){
		// Clear interrupt(XFERDONEPULSE bit)
		IO_PMC.SPI.INT_CLR.word |= D_DD_PMC_SPI_INT_XFERDONEPULSE_BIT;
		dd_pmc_spi_dsb();

		if(gDD_PMC_SPI_Info.pcallback_ss != NULL){
			((void(*)())gDD_PMC_SPI_Info.pcallback_ss)();
		}
	}

	// Tx FIFO Empty interrupt
	if( ( IO_PMC.SPI.INT_EN.bit.TXEMPTYPULSE == 1 ) && ( IO_PMC.SPI.INT_ST.bit.TXEMPTYPULSE == 1 ) ){
		// Clear interrupt(TXEMPTYPULSE bit)
		IO_PMC.SPI.INT_CLR.word |= D_DD_PMC_SPI_INT_TXEMPTYPULSE_BIT;
		dd_pmc_spi_dsb();

		if( gDD_PMC_SPI_Info.dma_wait == 2 ){
			// End SPI communication
			dd_pmc_spi_end_process();

			if(gDD_PMC_SPI_Info.psend_callback != NULL){
				((void(*)())gDD_PMC_SPI_Info.psend_callback)( gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_SEND].result );
			}

			return;
		}
		else {
			// Check remain data
			if( gDD_PMC_SPI_Info.send_pos < gDD_PMC_SPI_Info.num ){
				// Send data
				dd_pmc_spi_send_data();
			}
			else {
				
				if (gDD_PMC_SPI_Info.full_duplex) {
					IO_PMC.SPI.INT_EN.word &= ~(D_DD_PMC_SPI_INT_TX_CPU_BIT_SET);
					
					if( IO_PMC.SPI.CTRL.bit.MASTER == E_DD_PMC_SPI_TYPE_MASTER ){	// Master receiving
						// Set 1st dummy count
						if( gDD_PMC_SPI_Info.num < IO_PMC.SPI.FIFO_DPTH.bit.FIFODEPTH ){
							write_dummy = gDD_PMC_SPI_Info.num;
						}
						else {
							write_dummy = IO_PMC.SPI.FIFO_DPTH.bit.FIFODEPTH;
						}

						gDD_PMC_SPI_Info.dummy_count = write_dummy;

						// Dummy data for master receiving
						IO_PMC.SPI.TX_DWR.word = gDD_PMC_SPI_Info.dummy_count;
					}
				}
				else {
					// End SPI communication
					dd_pmc_spi_end_process();
				}

				if( gDD_PMC_SPI_Info.psend_callback != NULL ){
					((void(*)())gDD_PMC_SPI_Info.psend_callback)( D_DD_PMC_SPI_NORMAL_COMPLETE );
				}
				return;
			}
		}
	}

	// TX FIFO Watermark Level interrupt
	if( ( IO_PMC.SPI.INT_EN.bit.TXWMARKPULSE == 1 ) && ( IO_PMC.SPI.INT_ST.bit.TXWMARKPULSE == 1 ) ){
		// Clear interrupt(TXWMARKPULSE bit)
		IO_PMC.SPI.INT_CLR.word |= D_DD_PMC_SPI_INT_TXWMARKPULSE_BIT;
		dd_pmc_spi_dsb();

		// Check remain data
		if( gDD_PMC_SPI_Info.send_pos < gDD_PMC_SPI_Info.num ){
			// Send data
			dd_pmc_spi_send_data();
		}
	}

	// Receive FIFO Overflow interrupt
	if( ( IO_PMC.SPI.INT_EN.bit.RXFIFOOVERFLOW == 1 ) && ( IO_PMC.SPI.INT_ST.bit.RXFIFOOVERFLOW == 1 ) ){
		// Clear interrupt(RXFIFOOVERFLOW bit)
		IO_PMC.SPI.INT_CLR.word |= D_DD_PMC_SPI_INT_RXFIFOOVERFLOW_BIT;
		dd_pmc_spi_dsb();

		// End SPI communication
		dd_pmc_spi_end_process();

		if(gDD_PMC_SPI_Info.precv_callback != NULL){
			((void(*)())gDD_PMC_SPI_Info.precv_callback)( D_DD_PMC_SPI_RECV_OVERRUN_ERROR );
		}
		return;
	}

	// Rx FIFO Watermark Level interrupt
	if( ( ( IO_PMC.SPI.INT_EN.bit.RXWMARKPULSE == 1 ) && ( IO_PMC.SPI.INT_ST.bit.RXWMARKPULSE == 1 ) ) ||
		( ( IO_PMC.SPI.INT_EN.bit.RXFULLPULSE == 1 ) && ( IO_PMC.SPI.INT_ST.bit.RXFULLPULSE == 1 ) ) ||
		( ( IO_PMC.SPI.INT_EN.bit.RXTIMEOUT == 1 ) && ( IO_PMC.SPI.INT_ST.bit.RXTIMEOUT == 1 ) ) ){

		// Clear interrupt
		if( IO_PMC.SPI.INT_ST.bit.RXWMARKPULSE == 1 ){
			IO_PMC.SPI.INT_CLR.word |= D_DD_PMC_SPI_INT_RXWMARKPULSE_BIT;
			dd_pmc_spi_dsb();
		}
		if( IO_PMC.SPI.INT_ST.bit.RXFULLPULSE == 1 ){
			IO_PMC.SPI.INT_CLR.word |= D_DD_PMC_SPI_INT_RXFULLPULSE_BIT;
			dd_pmc_spi_dsb();
		}
		if( IO_PMC.SPI.INT_ST.bit.RXTIMEOUT == 1 ){
			IO_PMC.SPI.INT_CLR.word |= D_DD_PMC_SPI_INT_RXTIMEOUT_BIT;
			dd_pmc_spi_dsb();
		}

		if( gDD_PMC_SPI_Info.dma_wait == 1 ){
			if( IO_PMC.SPI.CTRL.bit.MASTER == E_DD_PMC_SPI_TYPE_MASTER ){	// Master receiving
				// Set remain dummy count
				if( gDD_PMC_SPI_Info.dummy_count < gDD_PMC_SPI_Info.num ){
					remain_dummy	= gDD_PMC_SPI_Info.num - gDD_PMC_SPI_Info.dummy_count;

					if( remain_dummy < IO_PMC.SPI.FIFO_DPTH.bit.FIFODEPTH ){
						write_dummy = remain_dummy;
					}
					else {
						write_dummy = IO_PMC.SPI.FIFO_DPTH.bit.FIFODEPTH;
					}

					gDD_PMC_SPI_Info.dummy_count += write_dummy;

					// Set Dummy data for master receiving
					IO_PMC.SPI.TX_DWR.word = write_dummy;
					dd_pmc_spi_dsb();
				}
			}
			return;
		}

		rx_wmark_set = IO_PMC.SPI.FIFO_WMK.bit.RXWMARKSET;

		// Get receive data
		if( IO_PMC.SPI.AUX_CTRL.bit.BITSIZE > E_DD_PMC_SPI_BIT_LEN_8 ){
			while( ( read_count < rx_wmark_set ) || ( IO_PMC.SPI.RXFIFO.bit.RX_FIFO_LEVEL >= rx_wmark_set ) ){
				// Read data word from the RX Serial Data Register.
				*(gDD_PMC_SPI_Info.recv_addr16 + read_count) = IO_PMC.SPI.RXDATA;
				read_count++;
			}
		}
		else {	// IO_PMC.SPI.AUX_CTRL.bit.BITSIZE <= E_DD_PMC_SPI_BIT_LEN_8
			while( ( read_count < rx_wmark_set ) || ( IO_PMC.SPI.RXFIFO.bit.RX_FIFO_LEVEL >= rx_wmark_set ) ){
				// Read data word from the RX Serial Data Register.
				*(gDD_PMC_SPI_Info.recv_addr8 + read_count) = IO_PMC.SPI.RXDATA;
				read_count++;
			}
		}

		gDD_PMC_SPI_Info.recv_pos += read_count;

		// Check remain data
		if( gDD_PMC_SPI_Info.recv_pos < gDD_PMC_SPI_Info.num ){
			remain_num = gDD_PMC_SPI_Info.num - gDD_PMC_SPI_Info.recv_pos;

			// All remain data is already exists in Rx FIFO
			if( remain_num <= IO_PMC.SPI.RXFIFO.bit.RX_FIFO_LEVEL ){
				// Get receive data
				if( IO_PMC.SPI.AUX_CTRL.bit.BITSIZE > E_DD_PMC_SPI_BIT_LEN_8 ){
					while( gDD_PMC_SPI_Info.recv_pos < gDD_PMC_SPI_Info.num ){
						// Read data word from the RX Serial Data Register.
						*(gDD_PMC_SPI_Info.recv_addr16 + gDD_PMC_SPI_Info.recv_pos) = IO_PMC.SPI.RXDATA;
						gDD_PMC_SPI_Info.recv_pos++;
					}
				}
				else {	// IO_PMC.SPI.AUX_CTRL.bit.BITSIZE <= E_DD_PMC_SPI_BIT_LEN_8
					while( gDD_PMC_SPI_Info.recv_pos < gDD_PMC_SPI_Info.num ){
						// Read data word from the RX Serial Data Register.
						*(gDD_PMC_SPI_Info.recv_addr8 + gDD_PMC_SPI_Info.recv_pos) = IO_PMC.SPI.RXDATA;
						gDD_PMC_SPI_Info.recv_pos++;
					}
				}

				// All data receive completed
				// End SPI communication
				dd_pmc_spi_end_process();

				if(gDD_PMC_SPI_Info.precv_callback != NULL){
					((void(*)())gDD_PMC_SPI_Info.precv_callback)( D_DD_PMC_SPI_NORMAL_COMPLETE );
				}
			}
			else {
				if( IO_PMC.SPI.CTRL.bit.MASTER == E_DD_PMC_SPI_TYPE_MASTER  ){	// Master receiving
					// Set remain dummy count
					if( gDD_PMC_SPI_Info.dummy_count < gDD_PMC_SPI_Info.num ){
						remain_dummy	= gDD_PMC_SPI_Info.num - gDD_PMC_SPI_Info.dummy_count;

						if( remain_dummy <= rx_wmark_set ){
							write_dummy = remain_dummy;

							// Update Rx Watermark Level
							IO_PMC.SPI.FIFO_WMK.bit.RXWMARKSET = remain_dummy - 1;
						}
						else {
							write_dummy = rx_wmark_set;
						}

						gDD_PMC_SPI_Info.dummy_count += write_dummy;

						// Dummy data for master receiving
						IO_PMC.SPI.TX_DWR.word = write_dummy;
						dd_pmc_spi_dsb();
					}
				}
			}
		}
		else {	// All data receive completed
			// End SPI communication
			dd_pmc_spi_end_process();

			if(gDD_PMC_SPI_Info.precv_callback != NULL){
				((void(*)())gDD_PMC_SPI_Info.precv_callback)( D_DD_PMC_SPI_NORMAL_COMPLETE );
			}
		}
	}
}


#ifdef CO_DDIM_UTILITY_USE
//---------------------------- utility section ---------------------------
/**
 * Calculate the clock divisor from the specified baudrate
 */
INT32 Dd_PMC_SPI_Calculate( ULONG baudrate, UCHAR* clk_div )
{
	UCHAR div;
	ULONG sclk;

#ifdef CO_PARAM_CHECK
	if( clk_div == NULL ){
		Ddim_Assertion(("SPI input param error. [clk_div] = NULL\n"));
		return D_DD_PMC_SPI_INPUT_PARAM_ERROR;
	}
#endif
	if ( Dd_PMC_CRG11_Get_CRPLC_PLLBYPASS()){
		// 32KHz.
		sclk = 32000 / (Dd_PMC_CRG11_Get_CRDM5_DIVMODE() + 1);
	}
	else {
		// 100MHz.
		sclk = 100000000 / (Dd_PMC_CRG11_Get_CRDM5_DIVMODE() + 1);
	}
	div = (sclk / baudrate / 2);

	if( div > 0 ){
		*clk_div = div -1;
	}
	else {
		*clk_div = 0;

		Ddim_Print(("Dd_PMC_SPI_Calculate() error. baudrate=%d , div=%d\n", baudrate, div));
		return D_DD_PMC_SPI_INPUT_PARAM_ERROR;
	}

	return D_DDIM_OK;
}


//---------------------------- colabo section ----------------------------
/**
 * Start sending data via DMA and SPI FIFO.
 */
INT32 Dd_PMC_SPI_Start_Send_DMA( UCHAR dma_ch, VP_PMC_SPI_CALLBACK pcallback )
{
	INT32	ret = D_DDIM_OK;

#ifdef CO_PARAM_CHECK
	if( dma_ch >= D_DD_PMC_HDMAC_CH_NUM_MAX ){
		Ddim_Assertion( ("SPI input param error. [dma_ch] = %d\n", dma_ch) );
		return D_DD_PMC_SPI_INPUT_PARAM_ERROR;
	}
#endif

	if( IO_PMC.SPI.CTRL.bit.DMA == 0 ){
		// For DMA operation
		Ddim_Print(("Dd_PMC_SPI_Start_Send_DMA() error. T_DD_PMC_SPI_CTRL.dma_en=0.\n"));
		return D_DD_PMC_SPI_STATUS_ABNORMAL;
	}

	memset((VOID*)&gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_SEND], 0, sizeof(gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_SEND]));	/* pgr0584 */

	// Set send DMA info
	dd_pmc_spi_set_send_dma( dma_ch );
	
	if (gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_SEND].count == 0) {
		// No send data.
		Ddim_Print(("Dd_PMC_SPI_Start_Send_DMA() error. no send data.\n"));
		return D_DD_PMC_SPI_STATUS_ABNORMAL;
	}

	gDD_PMC_SPI_Info.psend_callback = pcallback;

	// Set DMA wait status
	gDD_PMC_SPI_Info.dma_wait = 1;

	// Write the Enable Register to enable the SPI port
	IO_PMC.SPI.EN.word |= D_DD_PMC_SPI_EN_ENABLEREQ;
	dd_pmc_spi_dsb();
	
	// Update TC
	gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_SEND].hdmac_ctrl.config_a.bit.tc	= gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_SEND].count_info[0].trans_count - 1;

	// Start DMA
	ret = dd_pmc_spi_start_dma( dma_ch, (T_DD_PMC_HDMAC_CTRL*)&gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_SEND].hdmac_ctrl, (VP_CALLBACK)dd_pmc_spi_send_dma_callback );
	if( ret != D_DDIM_OK ){
		// DMA start error.
		Ddim_Print(("Dd_PMC_SPI_Start_Send_DMA() error. DMA start NG.\n"));
		// End SPI communication
		dd_pmc_spi_end_process();
	}

	return ret;
}


/**
 * Start Receive data via DMA and SPI FIFO.
 */
INT32 Dd_PMC_SPI_Start_Recv_DMA( UCHAR dma_ch, VP_PMC_SPI_CALLBACK pcallback )
{
	INT32	ret = D_DDIM_OK;
	UINT32	remain_dummy;
	UINT32	write_dummy;

#ifdef CO_PARAM_CHECK
	if( dma_ch >= D_DD_PMC_HDMAC_CH_NUM_MAX ){
		Ddim_Assertion( ("SPI input param error. [dma_ch] = %d\n", dma_ch) );
		return D_DD_PMC_SPI_INPUT_PARAM_ERROR;
	}
#endif

	if( IO_PMC.SPI.CTRL.bit.DMA == 0 ){
		// For DMA operation
		Ddim_Print(("Dd_PMC_SPI_Start_Recv_DMA() error. T_DD_PMC_SPI_CTRL.dma_en=0.\n"));
		return D_DD_PMC_SPI_STATUS_ABNORMAL;
	}

	memset((VOID*)&gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_RECV], 0, sizeof(gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_RECV]));

	// Set receive DMA info
	dd_pmc_spi_set_recv_dma( dma_ch );

	if (gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_RECV].count == 0) {
		// No receive data.
		Ddim_Print(("Dd_PMC_SPI_Start_Recv_DMA() error. no receive data.\n"));
		return D_DD_PMC_SPI_STATUS_ABNORMAL;
	}
	
	gDD_PMC_SPI_Info.precv_callback = pcallback;

	// Set DMA wait status
	gDD_PMC_SPI_Info.dma_wait = 1;

	// Init dummy count
	gDD_PMC_SPI_Info.dummy_count = 0;

	if( IO_PMC.SPI.CTRL.bit.MASTER == E_DD_PMC_SPI_TYPE_MASTER ){	// Master receiving
		// Set interrupt(RXWMARKPULSE, RXFULLPULSE_BIT, RXFIFOOVERFLOW, RXTIMEOUT_BIT bit)
		IO_PMC.SPI.INT_EN.word |= D_DD_PMC_SPI_INT_RX_DMA_BIT_SET;		// for Dummy data sending
	}

	// Write the Enable Register to enable the SPI port
	IO_PMC.SPI.EN.word |= D_DD_PMC_SPI_EN_ENABLEREQ;
	dd_pmc_spi_dsb();

	// Update TC
	gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_RECV].hdmac_ctrl.config_a.bit.tc	= gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_RECV].count_info[0].trans_count - 1;

	// Start DMA
	ret = dd_pmc_spi_start_dma( dma_ch, (T_DD_PMC_HDMAC_CTRL*)&gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_RECV].hdmac_ctrl, (VP_CALLBACK)dd_pmc_spi_recv_dma_callback );
	if( ret != D_DDIM_OK ){
		// DMA start error
		Ddim_Print(("Dd_PMC_SPI_Start_Recv_DMA() error. DMA start NG.\n"));
		
		// End SPI communication
		dd_pmc_spi_end_process();
		
		return ret;
	}

	if( IO_PMC.SPI.CTRL.bit.MASTER == E_DD_PMC_SPI_TYPE_MASTER ){	// Master receiving
		// SpinLock.
		Dd_ARM_Critical_Section_Start();

		// Set remain dummy count
		if( gDD_PMC_SPI_Info.dummy_count < gDD_PMC_SPI_Info.num ){
			remain_dummy	= gDD_PMC_SPI_Info.num - gDD_PMC_SPI_Info.dummy_count;

			if( remain_dummy < IO_PMC.SPI.FIFO_DPTH.bit.FIFODEPTH ){
				write_dummy = remain_dummy;
			}
			else {
				write_dummy = IO_PMC.SPI.FIFO_DPTH.bit.FIFODEPTH;
			}

			gDD_PMC_SPI_Info.dummy_count += write_dummy;

			// Set Dummy data for master receiving
			IO_PMC.SPI.TX_DWR.word = write_dummy;
			dd_pmc_spi_dsb();
		}

		// SpinUnLock.
		Dd_ARM_Critical_Section_End();
	}

	return ret;
}


/**
 * Start full duplex communication via DMA and SPI FIFO.
 */
INT32 Dd_PMC_SPI_Start_Full_Duplex_DMA( UCHAR dma_ch_send, UCHAR dma_ch_recv, VP_PMC_SPI_CALLBACK psend_callback, VP_PMC_SPI_CALLBACK precv_callback )
{
	INT32	ret;

#ifdef CO_PARAM_CHECK
	if( dma_ch_send >= D_DD_PMC_HDMAC_CH_NUM_MAX ){
		Ddim_Assertion(("SPI input param error. [dma_ch_send] = %d\n", dma_ch_send));
		return D_DD_PMC_SPI_INPUT_PARAM_ERROR;
	}
	if( dma_ch_recv >= D_DD_PMC_HDMAC_CH_NUM_MAX ){
		Ddim_Assertion(("SPI input param error. [dma_ch_recv] = %d\n", dma_ch_recv));
		return D_DD_PMC_SPI_INPUT_PARAM_ERROR;
	}
#endif

	if( IO_PMC.SPI.CTRL.bit.DMA == 0 ){
		// For DMA operation
		Ddim_Print(("Dd_PMC_SPI_Start_Full_Duplex_DMA() error. T_DD_PMC_SPI_CTRL.dma_en=0.\n"));
		return D_DD_PMC_SPI_STATUS_ABNORMAL;
	}

	memset((VOID*)&gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_SEND], 0, sizeof(gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_SEND]));		/* pgr0584 */
	memset((VOID*)&gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_RECV], 0, sizeof(gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_RECV]));
	
	// Set send DMA info
	dd_pmc_spi_set_send_dma( dma_ch_send );
	
	if (gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_SEND].count == 0) {
		// No send data.
		Ddim_Print(("Dd_PMC_SPI_Start_Full_Duplex_DMA() error. no send data.\n"));
		return D_DD_PMC_SPI_STATUS_ABNORMAL;
	}
	
	// Set receive DMA info
	dd_pmc_spi_set_recv_dma( dma_ch_recv );
	
	if (gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_RECV].count == 0) {
		// No receive data.
		Ddim_Print(("Dd_PMC_SPI_Start_Full_Duplex_DMA() error. no receive data.\n"));
		return D_DD_PMC_SPI_STATUS_ABNORMAL;
	}
	
	gDD_PMC_SPI_Info.psend_callback = psend_callback;
	gDD_PMC_SPI_Info.precv_callback = precv_callback;
	
	// Set fullduplex status
	gDD_PMC_SPI_Info.full_duplex = 1;
	
	// Set DMA wait status
	gDD_PMC_SPI_Info.dma_wait = 1;
	
	// Write the Enable Register to enable the SPI port
	IO_PMC.SPI.EN.word |= D_DD_PMC_SPI_EN_ENABLEREQ;
	dd_pmc_spi_dsb();
	
	// Update TC
	gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_SEND].hdmac_ctrl.config_a.bit.tc	= gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_SEND].count_info[0].trans_count - 1;
	gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_RECV].hdmac_ctrl.config_a.bit.tc	= gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_RECV].count_info[0].trans_count - 1;
	
	// Start send DMA
	ret = dd_pmc_spi_start_dma( dma_ch_send, (T_DD_PMC_HDMAC_CTRL*)&gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_SEND].hdmac_ctrl, (VP_CALLBACK)dd_pmc_spi_send_dma_callback );
	if( ret != D_DDIM_OK ){
		// DMA start error
		Ddim_Print(("Dd_PMC_SPI_Start_Full_Duplex_DMA() error. DMA send start NG.\n"));
		
		// End SPI communication
		dd_pmc_spi_end_process();
		
		return ret;
	}
	
	// Start receive DMA
	ret = dd_pmc_spi_start_dma( dma_ch_recv, (T_DD_PMC_HDMAC_CTRL*)&gDD_PMC_SPI_DMA_Info[D_DD_PMC_SPI_RECV].hdmac_ctrl, (VP_CALLBACK)dd_pmc_spi_recv_dma_callback );
	if( ret != D_DDIM_OK ){
		// DMA start error
		Ddim_Print(("Dd_PMC_SPI_Start_Full_Duplex_DMA() error. DMA receive start NG.\n"));
		
		// End SPI communication
		dd_pmc_spi_end_process();
		
		return ret;
	}
	
	return D_DDIM_OK;
}

#endif
